package seatiger.tomcat.util.net;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;

/**
 * 基于NIO启动的服务器
 */
public class NioEndpoint {
    /**
     * 初始化标记
     */
    protected boolean initialized = false;

    protected boolean running = false;

    protected ServerSocketChannel serverSocket;

    protected int port;

    protected long selectorTimeout = 1000;

    protected Executor executor = null;

    /**
     * 监听事件的线程数量
     */
    protected int pollerThreadCount;

    protected Poller[] pollers = null;

    /**
     * 处理器
     */
    protected NioChannelHandler handler;

    public void setHandler(NioChannelHandler handler) {
        this.handler = handler;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public void setExecutor(Executor executor) {
        this.executor = executor;
    }

    public Executor getExecutor() {
        return executor;
    }

    public void init() throws Exception {
        if (initialized) {
            return;
        }
        serverSocket = ServerSocketChannel.open();
        serverSocket.bind(new InetSocketAddress(port));
        //服务端还是使用的阻塞模式
        serverSocket.configureBlocking(true);

        if (pollerThreadCount <= 0) {
//            pollerThreadCount = Runtime.getRuntime().availableProcessors() * 2;
            pollerThreadCount = 1;
        }
        initialized = true;
    }

    public void start() throws Exception {
        if (!initialized) {
            init();
        }
        if (!running) {
            running = true;
            //启动独立的端口监听线程
            Thread acceptorThread = new Thread(new Acceptor(), "NIO-Acceptor");
            acceptorThread.setDaemon(true);
            acceptorThread.start();

            //初始化Poller线程
            pollers = new Poller[pollerThreadCount];
            for (int i = 0; i < pollerThreadCount; i++ ) {
                pollers[i] = new Poller();
                Thread pollerThread = new Thread(pollers[i], "Poller-" + i);
                pollerThread.setDaemon(true);
                pollerThread.start();
            }

        }
    }

    /**
     * 负责监听客户端的连接
     */
    protected class Acceptor implements Runnable {
        @Override
        public void run() {
            while (running) {
                try {
                    SocketChannel socket = serverSocket.accept();
                    if (running && socket != null) {
                        //处理socket
                        if (!processSocket(socket)) {
                            socket.close();
                        }
                    }
                } catch (Throwable t) {

                }
            }
        }
    }

    /**
     * 使用线程池 对socket处理
     *
     * @param socket
     * @return
     */
    protected boolean processSocket(SocketChannel socket) {
        try {
            executor.execute(new SocketOptionsProcessor(socket));
        } catch (Throwable throwable) {
            return false;
        }
        return true;
    }

    /**
     * 处理nioChannel,读取数据，解析报文
     * @return
     */
    protected boolean processSocket(NioChannel nioChannel){
        try {
            executor.execute(new NioChannelProcess(nioChannel));
        } catch (Throwable t) {
            return false;
        }
        return true;
    }

    /**
     * 处理 SocketChannel
     *
     * @return
     */
    protected boolean setSocketOptions(SocketChannel socket) {
        //将socket设置成非阻塞模式
        try {
            socket.configureBlocking(false);
            NioBufferHandler bufhandler = new NioBufferHandler(25188, 43800);
            NioChannel nioChannel = new NioChannel(socket, bufhandler);
            //将nioChannel 注册到Poller
            getPoller0().register(nioChannel);
            System.out.println("创建==》nioChannel" + nioChannel);
        } catch (Throwable t) {
            t.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 轮训获取Poller
     */
    protected int pollerRoundRobin = 0;
    public Poller getPoller0() {
        pollerRoundRobin = (pollerRoundRobin + 1) % pollers.length;
        Poller poller = pollers[pollerRoundRobin];
        return poller;
    }

   /**
     * 处理 SocketChannel
     * 主要将SocketChannel 封装成NioChannel
     */
    protected class SocketOptionsProcessor implements Runnable {
        private SocketChannel sc;

        public SocketOptionsProcessor(SocketChannel socket) {
            this.sc = socket;
        }

        @Override
        public void run() {
            if (!setSocketOptions(sc)) {
                try {
                    sc.socket().close();
                    sc.close();
                } catch (IOException ix) {
                }
            }
        }
    }

    /**
     * 读取数据，处理报文
     */
    protected class NioChannelProcess implements Runnable {
        protected NioChannel nioChannel;
        public NioChannelProcess(NioChannel nioChannel) {
            this.nioChannel = nioChannel;
        }

        @Override
        public void run() {
            handler.process(nioChannel);
        }
    }

    public class NioBufferHandler implements NioChannel.ApplicationBufferHandler {

        protected ByteBuffer readbuf = null;
        protected ByteBuffer writebuf = null;

        public NioBufferHandler(int readsize, int writesize) {
            //todo 这里要根据配置 分配不同类型的内存
            readbuf = ByteBuffer.allocate(readsize);
            writebuf = ByteBuffer.allocate(writesize);
        }

        @Override
        public ByteBuffer getReadBuffer() {
            return readbuf;
        }

        @Override
        public ByteBuffer getWriteBuffer() {
            return writebuf;
        }
    }

    public interface NioChannelHandler {
        void process(NioChannel nioChannel);
    }


    /**
     * 负责监听 并处理Selector上的事件
     */
    public class Poller implements Runnable {
        private ConcurrentLinkedQueue<PollerEvent> events = new ConcurrentLinkedQueue<>();
        protected Selector selector;

        public Poller() throws IOException {
            this.selector = Selector.open();
        }
        public Selector getSelector() {
            return selector;
        }
        public void register(final NioChannel socket) {
            socket.setPoller(this);
            KeyAttachment keyAttachment = new KeyAttachment(this, socket);
            PollerEvent event = new PollerEvent(socket, keyAttachment);
            addEvent(event);
        }
        public void addEvent(PollerEvent event) {
            events.add(event);
            selector.wakeup();
        }

        /**
         * 判断事件队列中有没有时间需要处理
         * @return
         */
        public boolean events() {
            boolean result = false;
            result = events.size() > 0;
            PollerEvent event = null;
            while ((event = events.poll()) != null) {
                try {
                    event.event();
                }catch (Exception e) {

                }
            }
            return result;
        }

        @Override
        public void run() {
            while (running) {
                //看一下事件队列中有没有需要处理的事件
                events();
                int keyCount = 0;
                try {
                    keyCount = selector.select(selectorTimeout);
                } catch (NullPointerException x) {
                    //sun bug 5076772 on windows JDK 1.5
                    if (selector == null) throw x;
                    continue;
                } catch (CancelledKeyException x) {
                    //sun bug 5076772 on windows JDK 1.5
                    if (selector == null) throw x;
                    continue;
                } catch (Throwable x) {
                    continue;
                }
                if (keyCount == 0) {
                    events();
                }
                Iterator<SelectionKey> iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;
                while (iterator != null && iterator.hasNext()) {
                    SelectionKey sk = iterator.next();
                    iterator.remove();
                    //获取关联的附件
                    KeyAttachment attachment = (KeyAttachment) sk.attachment();
                    try {
                        if (sk.isValid() && attachment != null) {
                            NioChannel channel = attachment.getChannel();
                            //处理数据
                            if (sk.isReadable()){
                                //就读取客户端发送过来的数据，并请求
                                /**
                                 * 这里如果只这样写，有个问题。因为这次异步的，流中数据还没有处理完
                                 * 但是这个循环会结束，然后select会再一次出发读事件，所以又会进入到此方法
                                 * 知道流中数据读取数据完成
                                 * 解决方法1： 这里使用单线程  new NioChannelProcess(channel).run() 这样的话这里又阻塞了
                                 * 解决方法2： 让SelectionKey不关注读事件（后续处理过程中 还是要在关注回来的）
                                 */
                                //不关注读事件
                                sk.interestOps(0);
                                boolean close = !processSocket(channel);
                                if (close) {
                                    //todo 关闭底层channel
                                }
                                /*SocketChannel channel = (SocketChannel) sk.channel();
                                NioChannel.ApplicationBufferHandler bufHandler = attachment.getNioChannel().bufHandler;
                                ByteBuffer buffer = bufHandler.getReadBuffer();
                                channel.read(buffer);
                                buffer.flip();
                                System.out.println(buffer);*/
                            }
                            //
                        }
                    } catch (Throwable throwable) {
                    }
                }
            }
        }
    }

    /**
     * 附件信息
     */
    public static class KeyAttachment {
        protected Poller poller;
        protected NioChannel channel;

        public KeyAttachment(Poller poller,NioChannel channel) {
            this.poller = poller;
            this.channel = channel;
        }

        public NioChannel getChannel() {
            return channel;
        }
    }

    /**
     * 事件
     */
    protected class PollerEvent {
        protected NioChannel socket;
        protected KeyAttachment attachment;

        public PollerEvent(NioChannel socket,KeyAttachment attachment) {
            this.socket = socket;
            this.attachment = attachment;
        }

        public void event(){
            try {
                System.out.println("register.....");
                //获取到对应的poller，并注册事件
                socket.getIOChannel().register(socket.getPoller().getSelector(),SelectionKey.OP_READ,attachment);
            }catch (Exception e){}

        }
    }


}
